package com.gzhuxj201.service.Impl;

import cn.hutool.json.*;
import com.gzhuxj201.entity.Course;
import com.gzhuxj201.entity.Result;
import com.gzhuxj201.entity.Student;
import com.gzhuxj201.entity.Teacher;
import com.gzhuxj201.mapper.CourseMapper;
import com.gzhuxj201.mapper.HomeWorkMapper;
import com.gzhuxj201.mapper.SCMapper;
import com.gzhuxj201.mapper.TCMapper;
import com.gzhuxj201.service.CourseService;
import com.gzhuxj201.util.RedisConstants;
import com.gzhuxj201.util.UserHolder;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.annotation.Transient;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
@Slf4j
public class CourseServiceImpl implements CourseService {
    @Autowired
    private CourseMapper courseMapper;
    @Autowired
    private SCMapper scMapper;
    @Autowired
    private TCMapper tcMapper;
    @Autowired
    private HomeWorkMapper homeWorkMapper;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private RedissonClient redissonClient;


    @Override
    public List<Course> selectByCnos(List<String> cnos) {
        if(cnos.isEmpty() || cnos==null){
            return Collections.emptyList();
        }
        return courseMapper.selectByCnos(cnos);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    //InterruptedException表示runtime异常,事务默认的回滚是出现runtime异常
    //为了避免事务失效,设置事务回滚的异常为Exception
    public int add(Course course) throws InterruptedException {
        //1.获取用户信息
        Teacher teacher = (Teacher) UserHolder.getUser();
        //2.更新教授表,新建课程的教师教授这门课
         int res= tcMapper.add(teacher.getTno(),course.getCno());

        //延时双删 删除缓存
        redisTemplate.delete(RedisConstants.COURSE_SELECTION_LIST_KEY);
        //删除课程
        int res4 =   courseMapper.add(course);
        //延时双删 再次删除缓存

        Thread.currentThread().sleep(500);

        redisTemplate.delete(RedisConstants.COURSE_SELECTION_LIST_KEY);

         //4.添加课程并返回结果
        return res4;
    }

    @Override
    public int updateByCno(Course course) throws InterruptedException {
        String cno = course.getCno();
        String key=RedisConstants.COURSE_STOCK_KEY+cno;
        String maxNumKey = RedisConstants.COURSE_INITNUM_KEY + cno;

    if(course.getCanSelect()==1){
        //1.如果开放选课,添加课程信息到redis
        //1.1 保存库存信息

        String stock = String.valueOf(course.getMaxNum() - course.getCurNum());
        stringRedisTemplate.opsForValue().set(key,stock);
        //不变的初始化库存,退课时,如果等于这个数了则不能再退课了
        stringRedisTemplate.opsForValue().set(maxNumKey,String.valueOf(course.getMaxNum()));

    }else {
        log.info("1: "+redisTemplate.delete(key));
        log.info("2: "+ redisTemplate.delete(maxNumKey));

    }
        //延时双删 删除缓存
        redisTemplate.delete(RedisConstants.COURSE_SELECTION_LIST_KEY);
        //删除课程
        int res4 =  courseMapper.updateByCno(course);
        //延时双删 再次删除缓存

            Thread.currentThread().sleep(500);

        redisTemplate.delete(RedisConstants.COURSE_SELECTION_LIST_KEY);

        //返回更新课程结果
        return res4;
    }

    @Override
    public Course selectByCno(String cno) {
        return courseMapper.selectByCno(cno);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int deleteByCno(String cno) throws InterruptedException {
        //删除选课表sc数据
       int res= scMapper.deleteByCno(cno);
        //删除教授表tc数据
        int res2= tcMapper.deleteByCno(cno);
        //删除课程下的作业项目
        int res3= homeWorkMapper.deleteByCno(cno);

        //延时双删 删除缓存
        redisTemplate.delete(RedisConstants.COURSE_SELECTION_LIST_KEY);
        //删除课程
        int res4 = courseMapper.deleteByCno(cno);
        //延时双删 再次删除缓存

            Thread.currentThread().sleep(500);

        redisTemplate.delete(RedisConstants.COURSE_SELECTION_LIST_KEY);
        //返回删除课程结果
        return res4;
    }

    @Override
    @Transactional
    public Result selectByCanSelect() {
        //1.获取学生信息
        Student student = (Student) UserHolder.getUser();
        //2.封装course的flag属性, true表示学生已选上这门课 false表示未选这门课
        //2.1 查询学生已选课程的所以课程号
        List<String> cnos = scMapper.selectBySno(student.getSno());
        HashSet<String> cnosSet = new HashSet<String>(cnos);

        //可以缓存课程的信息,但是学生是否已经选择了这门课不要缓存,因为每个学生的选课情况不一样
        List<Course> courseList=new ArrayList<>();
        //判断缓存是否存在
        JSONArray jsonArray1 = (JSONArray) redisTemplate.opsForValue().get(RedisConstants.COURSE_SELECTION_LIST_KEY);
        if(jsonArray1 != null){
            //缓存存在直接取来用
            courseList = JSONUtil.toList(jsonArray1, Course.class);
        }else {
            //对于热点key的缓存击穿问题,可以用<<逻辑过期时间>>和<<互斥锁>>方法来解决,
            // 这里用<<互斥锁>>方法来解决缓存击穿问题
            // 创建锁对象
            RLock redisLock =redissonClient.getLock("lock:cache:course");
            // 尝试获取锁
            boolean isLock = redisLock.tryLock();
            // 判断
            if (!isLock) {
                // 获取锁失败，直接返回失败或者重试
                log.error("服务器忙,请重试！");
                return Result.error("服务器忙,请重试");
            }

            try {
                //synchronized (this){
                    //缓存不存在,查询数据库
                    courseList = courseMapper.selectByCanSelect();
                    //保存在redis中  设置时间为3s
                    JSONArray jsonArray = JSONUtil.parseArray(courseList);
                    redisTemplate.opsForValue().set(RedisConstants.COURSE_SELECTION_LIST_KEY,jsonArray,2L, TimeUnit.SECONDS);
               //}
            } finally {
                redisLock.unlock();
            }

        }

        List<Course> courses = courseList.stream().map((item) -> {
            if (cnosSet.contains(item.getCno())) {
                //学生已经选了这门课,
                item.setFlag(true);
            } else {
                //学生没有选这门课
                item.setFlag(false);
            }
            return item;

        }).collect(Collectors.toList());

        return Result.success(courses);
    }
}
